package io.gitee.thant.utils;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

//这个类的stringify方法依赖jackson要注意
public class StringUtil {
	public static boolean isEmpty(String s) {
		return (null == s || "".equals(s));
	}
	
	public static String getUUID() {
		return UUID.randomUUID().toString().replaceAll("-", "");
	}
	
	public static String join(String join, String... parts) {
		StringBuilder sb = new StringBuilder();
		if (isEmpty(join)) {
			for (int i=0; i<parts.length; ++i) {
				sb.append(parts[i]);
			}
			return sb.toString();
		} else {
			for (int i=0; i<parts.length; ++i) {
				sb.append(join).append(parts[i]);
			}
			return sb.length()>0 ? sb.substring(join.length()) : "";
		}
	}

	public static String[] split(String source, final String split) {
		if (null == source) {
			String[] empty = new String[0];
			return empty;
		}
		
		int len = split.length();
		int begin = 0, num = 0, pos;
		int[] posA = new int[source.length()];
		while((pos = source.indexOf(split, begin))>=0) {
			posA[num++] = pos;
			begin = pos+len;
		}
		String[] ret = new String[num+1];
		begin = 0;
		for (int i=0; i<num; i++) {
			ret[i] = source.substring(begin, posA[i]);
			begin = posA[i]+len;
		}
		ret[num] = source.substring(begin);
		return ret;
	}
	
	public static String getTrace(Throwable e) {
		try (StringWriter sw = new StringWriter()) {
			try (PrintWriter pw = new PrintWriter(sw)) {
				e.printStackTrace(pw);
				return sw.toString();
			}
		} catch (Exception e1) {
			e1.printStackTrace();
			return null;
		}
	}
	
	/*
	System.out.println(StringUtils.getPart("123:456", ':', 0));
	System.out.println(StringUtils.getPart("123:456", ':', 1));
	System.out.println(StringUtils.getPart("123:456", ':', 2));
	
	System.out.println(StringUtils.getPart(":", ':', 0));
	System.out.println(StringUtils.getPart("123:", ':', 1));
	System.out.println(StringUtils.getPart(":456", ':', 2));
	
	System.out.println(StringUtils.getPart(":", ':', -1));
	System.out.println(StringUtils.getPart("123:", ':', 0));
	System.out.println(StringUtils.getPart(":456", ':', 1));
	*/
	public static String getPart(String s, char ch, int no) {
		if (null == s) return "";
		
		int idx = 0, begin = 0;
		int len = s.length();
		for (int i=0; i<len; i++) {
			if (ch == s.charAt(i)) {
				if (idx == no) {
					return s.substring(begin, i);
				}
				begin = i+1;
				idx ++;
			}
		}
		if (idx == no) {
			return s.substring(begin);
		}
		return "";
	}
	
	public static String repeat(String s, int n) {
		StringBuilder sb = new StringBuilder(s.length()*n);
		for (int i=0; i<n; ++i) {
			sb.append(s);
		}
		return sb.toString();
	}
	
	public static String repeat(char c, int n) {
		char[] dat = new char[n];
		for (int i=0; i<n; ++i) {
			dat[i]=c;
		}
		return new String(dat);
	}
	
	public static String concat(Object[] array, int begin, int end, String split) {
		StringBuilder sb = new StringBuilder();
		if (end<0 || end>array.length) {
			end = array.length;
		}
		for (int i=begin; i<end; ++i) {
			sb.append(split).append(array[i]);
		}
		return array.length>0 ? sb.toString().substring(split.length()) : "";
	}
	
	public static String concat(Object[] array, int begin, int end, char split) {
		return concat(array, begin, end, String.valueOf(split));
	}
	
	public static String URLEncode(String sour, String code) {
		if (sour != null) {
			try {
				return URLEncoder.encode(sour, code);
			} catch (Exception e) {
				LogHelper.error(e);
			}
		}
		return sour;
	}
	
	public static String URLDecode(String sour, String code) {
		if (sour != null) {
			try {
				return URLDecoder.decode(sour, code);
			} catch (Exception e) {
				LogHelper.error(e);
			}
		}
		return sour;
	}
	
	@SuppressWarnings("unchecked")
	public static <K,V,T> String stringify(Object value, StringBuilder sb) {
		if (null == sb) sb = new StringBuilder();

		if (null == value) {
			sb.append("null");
		} else if (value instanceof String
				|| value instanceof Number
				|| value instanceof Boolean
				|| value instanceof Character) {
			sb.append(value.toString());
		} else if (value instanceof Map) {
			Map<K, V> map = (Map<K, V>)value;
			Iterator<Entry<K, V>> it = map.entrySet().iterator();
			sb.append('{');
			while (it.hasNext()) {
				Map.Entry<K, V> m = it.next();
				K key = m.getKey();
				V val = m.getValue();
				stringify(key, sb);
				sb.append(':');
				stringify(val, sb);
				if (it.hasNext()) sb.append(',');
			}
			sb.append('}');
		} else if (value instanceof List) {
			List<?> lst = (List<?>)value;
			sb.append('[');
			for (int i=0; i<lst.size(); ++i) {
				stringify(lst.get(i), sb);
				if (i<lst.size()-1) sb.append(',');
			}
			sb.append(']');
		} else if (value instanceof Set) {
			Set<?> set = (Set<?>)value;
			Iterator<?> it = set.iterator();
			sb.append('(');
			while (it.hasNext()) {
				Object m = it.next();
				stringify(m, sb);
				if (it.hasNext()) sb.append(',');
			}
			sb.append(')');
		} else if (value.getClass().isArray()) {
			if (value.getClass() == int[].class) {
				sb.append(Arrays.toString((int[])value));
			} else if (value.getClass() == byte[].class) {
				sb.append(Arrays.toString((byte[])value));
			} else if (value.getClass() == char[].class) {
				sb.append(Arrays.toString((char[])value));
			} else if (value.getClass() == long[].class) {
				sb.append(Arrays.toString((long[])value));
			} else if (value.getClass() == float[].class) {
				sb.append(Arrays.toString((float[])value));
			} else if (value.getClass() == double[].class) {
				sb.append(Arrays.toString((double[])value));
			} else if (value.getClass() == short[].class) {
				sb.append(Arrays.toString((short[])value));
			} else if (value.getClass() == boolean[].class) {
				sb.append(Arrays.toString((boolean[])value));
			} else {
				//sb.append(Arrays.toString((T[])value)); //没有递归处理
				T[] array = (T[])value;
				sb.append('[');
				for (int i=0; i<array.length; ++i) {
					stringify(array[i], sb);
					if (i<array.length-1) sb.append(',');
				}
				sb.append(']');
			}
		} else {
			ObjectMapper mapper = new ObjectMapper();
			try {
				sb.append(mapper.writeValueAsString(value));
			} catch (JsonProcessingException e) {
				sb.append(value.toString());
			}
		}
		
		return sb.toString();
	}
	
	public static String MD5(String source) {
		return ByteUtil.MD5String(source.getBytes());
	}
	
	public static String substrIndex(String source, String... argA) {
		if (null == source) return "";
		
		int pos, begin=0, end=source.length();
		for (int i=0; i<argA.length; i+=2) {
			String split = argA[i];
			String style = argA[i+1];

			int b=0, e=style.length();
			boolean getLeft=false, getRight=false;
			if ('-' == style.charAt(0)) {
				getLeft = true;
				b = 1;
			}
			if ('-' == style.charAt(e-1)) {
				getRight = true;
				e--;
			}
			if (!getLeft && !getRight) return "";
			
			char dir = style.charAt(b);
			if (dir!='>' && dir!='<') return "";

			int sel = 1;
			try {
				sel = Integer.parseInt(style.substring(b+1, e));
				if (sel<=0) sel=1;
			} catch (Exception ept) {}
			
			int j = 1;
			if ('>'==dir) {
				for (pos=begin;;++j) {
					pos = source.indexOf(split, pos);
					if (pos<0 || pos+split.length()>end) return "";
					if (j==sel) break;
					pos += split.length();
				};
			} else {
				for (pos=end-1;;++j) {
					pos = source.lastIndexOf(split, pos);
					if (pos<begin) return "";
					if (j==sel) break;
					pos = pos-1;
				}
			}
			
			if (getLeft) {
				end = pos;
			} else {
				begin = pos+split.length();
			}
		}
		
		return source.substring(begin, end);
	}
	
	/*public static byte[] getBytes(String s) {
		if (null == s) return null;
		if (0==s.length()) return new byte[0];
		
		char c;
		char[] cA = s.toCharArray();
		byte[] bA = new byte[cA.length*2];
		for (int i=0, j=0; i<cA.length; ++i, j+=2) {
			c = cA[i];
			bA[j  ] = (byte)(c & 0xFF);
			bA[j+1] = (byte)((c & 0xFF00) >>> 8);
		}
		return bA;
	}

	public static String fromBytes(byte[] bA) {
		if (null == bA) return null;
		if (0==bA.length) return "";
		
		int len = bA.length/2;
		if (len*2 != bA.length) {
			byte[] newbA = new byte[bA.length+1];
			System.arraycopy(bA, 0, newbA, 0, bA.length);
			newbA[bA.length] = 0;
			bA = newbA;
		}
		
		char[] cA = new char[len];
		for (int i=0, j=0; i<len; ++i, j+=2) {
			cA[i] = (char)(bA[j]&0xFF | ((bA[j+1]&0xFF)<<8));
		}
		return new String(cA);
	}*/

	public static byte[] getBytes(String s) {
		if (null == s) return null;
		if (0==s.length()) return new byte[0];
		
		int i;
		byte[] bA = null;
		char[] cA = s.toCharArray();
		for (i=0; i<cA.length; ++i) {
			if (cA[i]>=128) {
				bA = new byte[cA.length*2+1];
				break;
			}
		}
		if (null == bA) {
			bA = new byte[cA.length+1];
			for (i=0; i<cA.length; ++i) {
				bA[i+1] = (byte)cA[i];
			}
			bA[0] = 0;
		} else {
			char c;
			for (i=0; i<cA.length; ++i) {
				c = cA[i];
				bA[i+1] = (byte)(c & 0xFF);
				bA[i+cA.length+1] = (byte)((c & 0xFF00) >>> 8);
			}
			bA[0] = (byte)0xFF;
		}
		
		return bA;
	}

	public static String fromBytes(byte[] bA) {
		if (null == bA) return null;
		if (0==bA.length) return "";
		
		if (0 == bA[0]) {
			int len = bA.length-1;
			char[] cA = new char[len];
			for (int i=0; i<len; ++i) {
				cA[i] = (char)bA[i+1];
			}
			return new String(cA);
		} else if (0xFF == bA[0]) {
			int len = (bA.length-1)/2;
			/*if (len*2 != bA.length-1) {
				byte[] newbA = new byte[bA.length+1];
				System.arraycopy(bA, 0, newbA, 0, bA.length);
				newbA[bA.length] = 0; //补0
				bA = newbA;
			}*/
			
			char[] cA = new char[len];
			for (int i=0, j=1; i<len; ++i, ++j) {
				cA[i] = (char)(bA[j]&0xFF | ((bA[j+len]&0xFF)<<8));
			}
			return new String(cA);
		} else {
			return null;
		}
	}
}
